﻿/*-------------------------------------------------------------------------------
 *	Author: ALEX RADU (alex.radu@gmail.com) 2010-2011
 * 
 *	Freeware: Please do not remove this header
 *
 *	Description:	Tandberg Visca Protocol Implementation dot.NET Way
 *  based on documentation from: 
 *  http://www.tandberg.com/collateral/documentation/Application_Programmer_Interfaces/TANDBERG_WAVE-II_API.pdf
 *  This is GPL software. Do with it as you want, but feed us back any improvements. 
 * 
 *  It supports all of the commands including UP, DOWN, IN, OUT, LEFT,
 *  RIGHT, NEAR, FAR, as well as all other extended commands.  
 * 
 *  To use this, you'll need a RS232 serial port. 
 * 
 * ------------------------------------------------------------------------------*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using CCTV.Common;

namespace CCTV.Protocols
{
    public class TandbergVisca
    {


        const byte camId = 0x81;

        private readonly static byte FixedPart = 0x01;
        private readonly static byte Terminator = 0xFF;

        #region PanTZ Commands
        private byte[] SetAutoFocus = { 0x04, 0x38, 0x02 };
        private byte[] SetManualFocus = { 0x04, 0x38, 0x03 };

        private byte[] FocusNear = { 0x04, 0x08, 0x02 };
        private byte[] FocusFar = { 0x04, 0x08, 0x03 };
        private byte[] FocusStop = { 0x04, 0x38, 0x00 };

        private byte[] IrisOpen = { 0x04, 0x0A, 0x02 };
        private byte[] IrisClose = { 0x04, 0x0A, 0x03 };
        private byte[] IrisReset = { 0x04, 0x0A, 0x00 };

        private byte[] ZoomTele = { 0x04, 0x07, 0x20 }; //add speed also...
        private byte[] ZoomWide = { 0x04, 0x07, 0x30 }; //add speed also...
        private byte[] ZoomStop = { 0x04, 0x07, 0x00 };


        private byte[] TiltUp = { 0x06, 0x01, 0x00, 0x00, 0x03, 0x01 }; //add p/t speed also...
        private byte[] TiltDown = { 0x06, 0x01, 0x00, 0x00, 0x03, 0x02 }; //add p/t speed also...
        private byte[] PanRight = { 0x06, 0x01, 0x00, 0x00, 0x02, 0x03 }; //add p/t speed also...
        private byte[] PanLeft = { 0x06, 0x01, 0x00, 0x00, 0x01, 0x03 }; //add p/t speed also...

        private byte[] StopMoving = { 0x06, 0x01, 0x03, 0x03, 0x03, 0x03 };

        private byte[] IROn = { 0x06, 0x08, 0x02 };
        private byte[] IROff = { 0x06, 0x08, 0x03 };

        private byte[] Home = { 0x06, 0x04 };

        private byte[] StorePreset = { 04, 0x3F, 0x01, 0x00 }; //ad preset nr @ the end...
        private byte[] GetPreset = { 04, 0x3F, 0x02, 0x00 }; //ad preset nr @ the end...

        private byte[] SetBackLightOn = { 0x04, 0x33, 0x02 };
        private byte[] SetBackLightOff = { 0x04, 0x33, 0x03 };


        private byte[] SetAWBOn = { 0x04, 0x35, 0x00 };
        private byte[] SetAWBOff = { 0x04, 0x35, 0x05 };

        #endregion

        #region Enums
        public enum PresetAction { Set, Goto }
        public enum Action { Start, Stop }
        public enum SwitchAction { On, Off }
        public enum Focus { Near, Far, Stop }
        public enum Zoom { Wide, Tele, Stop }
        public enum Tilt { Up, Down }
        public enum Pan { Left, Right }
        public enum Iris { Open, Close, Reset }
        #endregion

        #region Extended Command Set
        public byte[] Preset(uint deviceAddress, byte preset, PresetAction action)
        {
            byte[] presetCommand = GetPreset;
            switch (action)
            {
                case PresetAction.Set:
                    presetCommand = StorePreset;
                    break;
                case PresetAction.Goto:
                    presetCommand = GetPreset;
                    break;
                default:
                    presetCommand = GetPreset;
                    break;
            }
            //change the position, @ the end...
            presetCommand[presetCommand.Length - 1] = preset;

            return Message.GetMessage(deviceAddress, presetCommand);
        }


        public byte[] ZeroPanPosition(uint deviceAddress)
        {
            return Message.GetMessage(deviceAddress, Home);
        }



        public byte[] AutoFocus(uint deviceAddress, SwitchAction action)
        {
            if (action == SwitchAction.On)
                return Message.GetMessage(deviceAddress, SetAutoFocus);
            else
                return Message.GetMessage(deviceAddress, SetManualFocus);

        }
        public byte[] AutoIris(uint deviceAddress)
        {
            return Message.GetMessage(deviceAddress, IrisReset);
        }

        public byte[] InfraRed(uint deviceAddress, SwitchAction action)
        {
            if (action == SwitchAction.On)
                return Message.GetMessage(deviceAddress, IROn);
            else
                return Message.GetMessage(deviceAddress, IROff);
        }

        public byte[] BackLightCompensation(uint deviceAddress, SwitchAction action)
        {
            if (action == SwitchAction.On)
                return Message.GetMessage(deviceAddress, SetBackLightOn);
            else
                return Message.GetMessage(deviceAddress, SetBackLightOff);
        }
        public byte[] AutoWhiteBalance(uint deviceAddress, SwitchAction action)
        {
            if (action == SwitchAction.On)
                return Message.GetMessage(deviceAddress, SetAWBOn);
            else
                return Message.GetMessage(deviceAddress, SetAWBOff);
        }

        #endregion

        #region Base Command Set


        public byte[] CameraIrisSwitch(uint deviceAddress, Iris action)
        {
            if (action == Iris.Close)
                return Message.GetMessage(deviceAddress, IrisClose);
            else
                return Message.GetMessage(deviceAddress, IrisOpen);
        }

        public byte[] CameraFocus(uint deviceAddress, Focus action)
        {
            if (action == Focus.Near)
                return Message.GetMessage(deviceAddress, FocusNear);
            else if (action == Focus.Far)
                return Message.GetMessage(deviceAddress, FocusFar);
            else
                return Message.GetMessage(deviceAddress, FocusStop);
        }

        public byte[] CameraZoom(uint deviceAddress, Zoom action, byte speed)
        {
            if (action == Zoom.Tele)
                return Message.GetMessage(deviceAddress, ChangeByte(ZoomTele, 2, speed));
            else if (action == Zoom.Wide)
                return Message.GetMessage(deviceAddress, ChangeByte(ZoomWide, 2, speed));
            else
                return Message.GetMessage(deviceAddress, ZoomStop);
        }




        public byte[] CameraTilt(uint deviceAddress, Tilt action, byte speed)
        {
            if (action == Tilt.Up)
            {
                return Message.GetMessage(deviceAddress, ChangeByte(TiltUp, 3, speed));
            }
            else if (action == Tilt.Down)
                return Message.GetMessage(deviceAddress, ChangeByte(TiltDown, 3, speed));
            else
                return Message.GetMessage(deviceAddress, StopMoving);
        }

        private static byte[] ChangeByte(byte[] cmd, int position, byte valToAdd)
        {
            byte[] newByte = new byte[cmd.Length];
            cmd.CopyTo(newByte, 0);
            newByte[position] = (byte)(newByte[position] + valToAdd);
            return newByte;
        }



        public byte[] CameraPan(uint deviceAddress, Pan action, byte speed)
        {
            if (action == Pan.Left)
            {
                return Message.GetMessage(deviceAddress, ChangeByte(PanLeft, 2, speed));
            }
            else if (action == Pan.Right)
                return Message.GetMessage(deviceAddress, ChangeByte(PanRight, 2, speed));
            else
                return Message.GetMessage(deviceAddress, StopMoving);
        }


        public byte[] CameraStopMoving(uint deviceAddress)
        {
            return Message.GetMessage(deviceAddress, StopMoving);
        }



        #endregion

        public struct Message
        {
            public static byte Address;
            public static byte[] EndCommand;

            public static byte[] GetMessage(uint address, byte[] command)
            {
                if (address < 1 & address > 8)
                    throw new Exception("Tandberg/VISCA supports 8 devices only");

                Address = 0x81; // (byte)(80 + address);
                EndCommand = new byte[command.Length + 3];
                EndCommand[0] = Address;
                EndCommand[1] = FixedPart;
                EndCommand[EndCommand.Length - 1] = Terminator;
                for (int i = 0; i < command.Length; i++)
                {
                    EndCommand[i + 2] = command[i];
                }

                return EndCommand;
            }

        }
    }
}
